/***************************************************************
*
* DS_BIT_STREAM.CPP
*
* Memory bit stream implementation
***************************************************************/
#include <ap/settings/ds_settings.h>

#include "ds/ds_bit_stream.h"
#include "ds_self_linked.h"
#include "ds_vector.h"

template<class T>
void swap(T &v1, T &v2) { T t = v2; v2 = v1; v1 = t; }

namespace ds {
#ifdef ENABLE_BIT_STREAM_PROFILE
   BIT_STREAM_PROFILE * ds::SINGLETON<BIT_STREAM_PROFILE>::ms_instance = 0;
   static BIT_STREAM_PROFILE gs_profileInstance;
#endif

/************************************
* BIT_STREAM::BIT_STREAM
*
************************************/
BIT_STREAM::BIT_STREAM()
   : m_pData(0)
   , m_size(0)
   , m_pos(0)
   , m_isWrite(false)
   , m_isOwnBuffer(false)
   , m_isOverflow(false)
   , m_isInitialized(false)
   , m_reordering(false)
{
}

/************************************
* BIT_STREAM::BIT_STREAM
*
************************************/
BIT_STREAM::BIT_STREAM(MODE_READ mode, const void* data, UINT sizeBytes, UINT startOffset/* = 0*/)
   : m_pData(const_cast<BYTE*>( (const BYTE*) data ))
   , m_size(sizeBytes)
   , m_pos(startOffset)
   , m_isWrite(false)
   , m_isOwnBuffer(false)
   , m_isOverflow(false)
   , m_isInitialized(true)
   , m_reordering(false)
{
}

/************************************
* BIT_STREAM::BIT_STREAM
*
************************************/
BIT_STREAM::BIT_STREAM(MODE_WRITE mode, void* data, UINT sizeBytes, UINT startOffset/* = 0*/)
   : m_pData( (BYTE*)data )
   , m_size(sizeBytes)
   , m_pos(startOffset)
   , m_isWrite(true)
   , m_isOwnBuffer(false)
   , m_isOverflow(false)
   , m_isInitialized(true)
   , m_reordering(false)
{
}

/************************************
* BIT_STREAM::BIT_STREAM
*
************************************/
BIT_STREAM::BIT_STREAM(MODE_WRITE mode, UINT sizeBytes/* = START_SIZE*/)
   : m_pData(apNEW BYTE[sizeBytes])
   , m_size(sizeBytes)
   , m_pos(0)
   , m_isWrite(true)
   , m_isOwnBuffer(true)
   , m_isOverflow(false)
   , m_isInitialized(true)
   , m_reordering(false)
{
}

/*************************************************************
* BIT_STREAM::BIT_STREAM()
* 
*************************************************************/
BIT_STREAM::BIT_STREAM(const BIT_STREAM& stream)
   : m_pData(0)
   , m_size(0)
   , m_pos(0)
   , m_isWrite(false)
   , m_isOwnBuffer(false)
   , m_isOverflow(false)
   , m_isInitialized(false)
   , m_reordering(false)
{
   if (stream.IsInited()) {
      m_isInitialized   = true;
      m_isWrite         = stream.m_isWrite;
      m_pos             = stream.m_pos;
      m_isOverflow      = stream.m_isOverflow;
      m_reordering      = stream.m_reordering;

      if (stream.GetSize() > 0) {
         m_size         = stream.GetSize();
         m_isOwnBuffer  = true;   
         m_pData = apNEW BYTE[m_size];
         memcpy(m_pData, stream.m_pData, m_size);
      }
   }
}


/************************************
* BIT_STREAM::InitWrite
*
************************************/
void BIT_STREAM::InitWrite(UINT sizeBytes /*= 512*/)
{
   if (!m_isOwnBuffer || sizeBytes < m_size) {
      if (m_isOwnBuffer) {
         delete [] m_pData;
      }
      m_pData = apNEW BYTE[sizeBytes];
      m_size = sizeBytes;
   }

   m_pos = 0;
   m_isWrite = true;
   m_isOwnBuffer = true;
   m_isOverflow = false;
   m_isInitialized = true;
}

/************************************
* BIT_STREAM::InitWrite
*
************************************/
void BIT_STREAM::InitWrite(void* data, UINT sizeBytes, UINT startOffset /*= 0*/)
{
   if (m_isOwnBuffer) {
      delete [] m_pData;
   }

   m_pData = (BYTE*)data;
   m_size = sizeBytes;
   m_pos = startOffset;
   m_isWrite = true;
   m_isOwnBuffer = false;
   m_isOverflow = false;
   m_isInitialized = true;
}

/************************************
* BIT_STREAM::InitRead
*
************************************/
void BIT_STREAM::InitRead(const void* data, UINT sizeBytes, UINT startOffset /*= 0*/)
{
   if (m_isOwnBuffer) {
      delete [] m_pData;
   }

   m_pData = const_cast<BYTE*>( (const BYTE*) data );
   m_size = sizeBytes;
   m_pos = startOffset;
   m_isWrite = false;
   m_isOwnBuffer = false;
   m_isOverflow = false;
   m_isInitialized = true;
}

/************************************
* BIT_STREAM::~BIT_STREAM
*
************************************/
BIT_STREAM::~BIT_STREAM()
{
   if(m_isOwnBuffer) {
      delete [] m_pData;
   }
}

/*************************************************************
* BIT_STREAM::Swap()
* 
*************************************************************/
void BIT_STREAM::Swap(BIT_STREAM &stream)
{
   swap(m_pData, stream.m_pData);
   swap(m_size, stream.m_size);
   swap(m_pos, stream.m_pos);
   swap(m_isWrite, stream.m_isWrite);
   swap(m_isOwnBuffer, stream.m_isOwnBuffer);
   swap(m_isOverflow, stream.m_isOverflow);
   swap(m_isInitialized, stream.m_isInitialized);

   return;
}

/************************************
* BIT_STREAM::RawWrite
*
************************************/
void BIT_STREAM::RawWrite(const void* data, UINT sizeBits, UINT offsetBits /*= 0*/ )
{
   STRONG_ASSERT(!m_isOverflow && m_isWrite && m_isInitialized);
   if (m_isOverflow || !m_isWrite) {
      return;
   }

   if (BITS_TO_BYTES(m_pos + sizeBits) >= m_size) {
      if (!m_isOwnBuffer) {
         // Error
         STRONG_ASSERT(0);
         m_isOverflow = true;
         return;
      } else {
         // Grow
         UINT needSize = BITS_TO_BYTES(m_pos + sizeBits);
         UINT newSize = 0;
         for (int i = 31; i > 0; --i) {
            if( needSize & (1 << i) ) {
               newSize = (1 << (i+1));
               break;
            }
         }
         STRONG_ASSERT(newSize>=needSize);
         BYTE * newRep = apNEW BYTE[newSize];
         memcpy(newRep, m_pData, m_size);
         m_size = newSize;
         delete [] m_pData;
         m_pData = newRep;
      }
   }
   CopyBits(m_pData, m_pos, data, offsetBits, sizeBits);
   BIT_PROFILE_ADD("RawWrite", sizeBits);
   m_pos+=sizeBits;
}

/************************************
* BIT_STREAM::RawRead
*
************************************/
void BIT_STREAM::RawRead(void* data, UINT sizeBits, UINT dstOffset /*= 0*/)
{
   STRONG_ASSERT(!m_isWrite && !m_isOverflow && m_isInitialized);
   if (m_isWrite || m_isOverflow)
      return;

   if ((m_pos + sizeBits) > BYTES_TO_BITS(m_size)) {
      STRONG_ASSERT(0);
      m_isOverflow = true;
      return;
   }

   CopyBits(data, dstOffset, m_pData, m_pos, sizeBits);
   m_pos+=sizeBits;
}

/*************************************************************
* BIT_STREAM::WriteStream()
* 
*************************************************************/
void BIT_STREAM::WriteStream(const BIT_STREAM &stream)
{
   RawWrite(stream.GetData(), stream.GetPos());
   return;
}

/*************************************************************
* BIT_STREAM::WritePadding()
* 
*************************************************************/
void BIT_STREAM::WritePadding(void)
{
   if (m_pos % 8 == 0) {
      return;
   }
   BYTE buf = 0;
   RawWrite(&buf, 8 - m_pos % 8);

   return;
}

/*************************************************************
* BIT_STREAM::SkipPadding()
* 
*************************************************************/
void BIT_STREAM::SkipPadding(void)
{
   if (m_pos % 8 == 0) {
      return;
   }
   BYTE buf = 0;
   RawRead(&buf, 8 - m_pos % 8);

   return;
}

/************************************
* BIT_STREAM::GetData
*
************************************/
void *BIT_STREAM::GetData()
{
   return m_pData;
}

/*************************************************************
* BIT_STREAM::GetData()
* 
*************************************************************/
const void *BIT_STREAM::GetData(void) const
{
   return m_pData;
}

/************************************
* BIT_STREAM::GetData
*
************************************/
void *BIT_STREAM::Tell()
{
   return m_pData + GetPosBytes();
}

/************************************
* BIT_STREAM::GetSize
*
************************************/
UINT BIT_STREAM::GetSize() const
{
   return m_size;
}

/************************************
* BIT_STREAM::GetPos
*
************************************/
UINT BIT_STREAM::GetPos() const
{
   return m_pos;
}

/************************************
* BIT_STREAM::GetPosBytes
*
************************************/
UINT BIT_STREAM::GetPosBytes(void) const
{
   return BITS_TO_BYTES(m_pos);
}

/************************************
* BIT_STREAM::SetPos
*
************************************/
void BIT_STREAM::SetPos(UINT pos)
{
   m_pos = pos;
}

/************************************
* BIT_STREAM::IsOverflow
*
************************************/
bool BIT_STREAM::IsOverflow() const
{
   return m_isOverflow;
}

/************************************
* BIT_STREAM::IsStoring
*
************************************/
bool BIT_STREAM::IsStoring() const
{
   return m_isWrite;
}

/************************************
* BIT_STREAM::IsStoring
*
************************************/
void BIT_STREAM::Clear()
{
   m_pos = 0;
}

/**************************************************************
* BIT_STREAM::operator=()
*
**************************************************************/
BIT_STREAM& BIT_STREAM::operator=(const BIT_STREAM& stream)
{
   if(m_isOwnBuffer) {
      delete [] m_pData;
   }
   
   m_pData = 0;
   m_size = 0;
   m_pos = 0;
   m_isWrite = false;
   m_isOwnBuffer = false;
   m_isOverflow = false;
   m_isInitialized = false;

   if (stream.IsInited()) {
      m_isInitialized   = true;
      m_isWrite       = stream.m_isWrite;
      m_pos             = stream.m_pos;
      m_isOverflow      = stream.m_isOverflow;

      if (stream.GetSize() > 0) {
         m_size         = stream.GetSize();
         m_isOwnBuffer  = true;   
         m_pData = apNEW BYTE[m_size];
         memcpy(m_pData, stream.m_pData, m_size);
      }
   }

   return *this;
}

/************************************
* CopyBits
*
************************************/
void CopyBits(void *pDestVoid, UINT destOffset, const void *pSrcVoid, UINT srcOffset, UINT sizeBits )
{
   BYTE *pDest = reinterpret_cast<BYTE *>(pDestVoid);
   const BYTE *pSrc = reinterpret_cast<const BYTE *>(pSrcVoid);

   if (sizeBits==0)
      return;

   STRONG_ASSERT(pDest!=0);
   STRONG_ASSERT(pSrc!=0);

   // If aligned
   if (sizeBits%8==0 && destOffset%8==0 && srcOffset%8==0) {
      memcpy(pDest + destOffset/8, pSrc + srcOffset/8, sizeBits/8);
      return;
   }

   // Adjust pointers and offsets
   {
      UINT destOffsetBytes = destOffset/8;
      pDest += destOffsetBytes;
      destOffset -= destOffsetBytes*8;

      UINT srcOffsetBytes = srcOffset/8;
      pSrc += srcOffsetBytes;
      srcOffset -= srcOffsetBytes*8;
   }

   UINT restDest = 8 - destOffset;

   // Copy less then 1 byte
   if ( restDest >= sizeBits ) {
      char destPart;
      if ( destOffset >= srcOffset ) {
         destPart = char(*pSrc << (destOffset-srcOffset));
      } else if ( srcOffset+sizeBits <= 8 ) {
         destPart = char(*pSrc >> (srcOffset-destOffset));
      } else {
         const size_t offset = srcOffset-destOffset;
         const char srcLoMask = char(~(int(-1)<<(8-offset) ));
         destPart = char((*pSrc >> offset) & srcLoMask);
         ++pSrc;
         destPart |= *pSrc << (8-offset);
      }

      const char destMask = char( (int(-1)<<destOffset) & ~(int(-1)<<(destOffset+sizeBits)) );
      *pDest = *pDest & (~destMask) | destPart & destMask;
      return;
   }

   // First not full byte
   if ( destOffset > 0 ) {
      char destPart;
      if ( destOffset >= srcOffset ) {
         destPart = char(*pSrc << (destOffset-srcOffset));
         srcOffset += restDest;
      } else if ( destOffset == srcOffset ) {
         destPart = *pSrc;
         ++pSrc;
         srcOffset = 0;
      } else {
         const size_t offset = srcOffset-destOffset;
         const char srcLoMask = char(~(int(-1)<<(8-offset) ));
         destPart = char((*pSrc >> offset) & srcLoMask);
         ++pSrc;
         destPart |= *pSrc << (8-offset);
         srcOffset -= destOffset;
      }

      const char destMask = char( int(-1)<<destOffset );
      *pDest = *pDest & (~destMask) | destPart & destMask;
      ++pDest;
      destOffset = 0;
      sizeBits -= restDest;
   }

   // Middle bytes
   // Copy by one byte (try by words?)
   if (srcOffset>0) {
      const char srcLoMask = char(~(int(-1)<<(8-srcOffset) ));
      while ( sizeBits >= 8 ) {
         *pDest = char((*pSrc >> srcOffset) & srcLoMask);
         ++pSrc;
         *pDest |= *pSrc << (8-srcOffset);

         ++pDest;
         sizeBits -= 8;
      }

      // Last not full byte
      if ( sizeBits>0 ) {
         char destPart = char((*pSrc >> srcOffset) & srcLoMask);
         if ( srcOffset + sizeBits > 8 ) {
            ++pSrc;
            destPart |= *pSrc << (8-srcOffset);
         }

         const char destMask = char( ~(int(-1)<<sizeBits) );
         *pDest = *pDest & (~destMask) | destPart & destMask;
      }
   }
   else {
      // Middle bytes
      while ( sizeBits >= 8 ) {
         *pDest = *pSrc;
         ++pSrc;
         ++pDest;
         sizeBits -= 8;
      }

      // Last not full byte
      if ( sizeBits>0 ) {
         const char destMask = char( ~(int(-1)<<sizeBits) );
         *pDest = *pDest & (~destMask) | *pSrc & destMask;
      }
   }
}

}
//
// End-of-file DS_BIT_STREAM.CPP
//
